package cn.starrysky108.simpleerp.pub.service.impl;

import cn.starrysky108.simpleerp.core.constant.RedisKeys;
import cn.starrysky108.simpleerp.core.enums.ResultCodeEnum;
import cn.starrysky108.simpleerp.core.exception.TokenException;
import cn.starrysky108.simpleerp.core.security.UserDetailsEntity;
import cn.starrysky108.simpleerp.core.util.JwtUtil;
import cn.starrysky108.simpleerp.core.util.RedisUtil;
import cn.starrysky108.simpleerp.pub.request.RefreshTokenRequest;
import cn.starrysky108.simpleerp.pub.request.UserRegisterRequest;
import cn.starrysky108.simpleerp.pub.response.TokenResponse;
import cn.starrysky108.simpleerp.pub.service.AuthService;
import cn.starrysky108.simpleerp.system.entity.SystemPermission;
import cn.starrysky108.simpleerp.system.entity.SystemRole;
import cn.starrysky108.simpleerp.user.entity.User;
import cn.starrysky108.simpleerp.user.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 实现UserDetailsService用于spring security
 *
 * @author Carl Lee
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService, UserDetailsService {
    private final UserService userService;

    @Autowired
    public AuthServiceImpl(UserService userService) {
        this.userService = userService;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TokenResponse getToken(RefreshTokenRequest refreshTokenRequest) {
        String refreshToken = refreshTokenRequest.getRefreshToken();
        Long userId = JwtUtil.getUserId(refreshToken);
        String username = JwtUtil.getUserName(refreshToken);
        if (RedisUtil.hasKey(RedisKeys.TOKEN_DENNY + refreshToken)) {
            throw new TokenException(refreshToken, ResultCodeEnum.TOKEN_ILLEGAL);
        }
        TokenResponse tokenResponse = TokenResponse.builder()
                .refreshToken(JwtUtil.getRefreshToken(username, userId))
                .accessToken(JwtUtil.getAccessToken(username, userId))
                .build();
        //将旧token放入token黑名单中，使其失效
        RedisUtil.addToken(RedisKeys.TOKEN_DENNY, refreshToken);
        if (StringUtils.hasText(refreshTokenRequest.getAccessToken())) {
            RedisUtil.addToken(RedisKeys.TOKEN_DENNY, refreshTokenRequest.getAccessToken());
        }
        // 删除旧token的登录信息
        RedisUtil.hDel(RedisKeys.ONLINE_USER + userId, refreshToken);
        return tokenResponse;
    }

    @Override
    public TokenResponse register(UserRegisterRequest userRegisterRequest) {
        assert userRegisterRequest != null;
        //对原始密码进行加密
        String pwd = JwtUtil.encryptPassword(userRegisterRequest.getPassword());
        userRegisterRequest.setPassword(pwd);
        //保存user,遇到错误时直接跑出
        User user = User.builder().build();
        BeanUtils.copyProperties(userRegisterRequest, user);
        userService.save(user);
        TokenResponse tokenResponse = TokenResponse.builder()
                .refreshToken(JwtUtil.getRefreshToken(user.getUsername(), user.getId()))
                .accessToken(JwtUtil.getAccessToken(user.getUsername(), user.getId()))
                .user(userService.getUserVo(user.getId()))
                .build();

        //写入redis
        RedisUtil.sAdd(RedisKeys.ONLINE_USER + user.getId(), tokenResponse.getRefreshToken());
        return tokenResponse;
    }

    @Override
    public void logout(String refreshToken) {
        List<String> error = new ArrayList<>();
        Long userId = JwtUtil.getUserId(refreshToken);
        //将token加入黑名单
        RedisUtil.addToken(RedisKeys.TOKEN_DENNY + userId, refreshToken);
        try {
            //删除在线设备信息
            RedisUtil.hDel(RedisKeys.ONLINE_USER + userId, refreshToken);

        } catch (Exception e) {
            error.add("删除在线设备信息出错:" + userId);
        }
        //记录错误信息
        if (log.isWarnEnabled()) {
            error.forEach(log::warn);
        }
    }


    @Override
    public Boolean hasUser(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userService.getOne(queryWrapper);
        return !ObjectUtils.isEmpty(user);
    }

    @Override
    public List<SimpleGrantedAuthority> getUserAuthorityInfo(Long userId) {
        String key = RedisKeys.PERMS + userId;
        String auth = (String) RedisUtil.get(key);
        if (StringUtils.hasText(auth)) {

            return Arrays.stream(auth.split(","))
                    .map(SimpleGrantedAuthority::new).collect(Collectors.toList());

        }
        List<SystemRole> roles = userService.listRoleByUserId(userId);
        List<SystemPermission> permissions = userService.listPermissionByUserId(userId);
        List<SimpleGrantedAuthority> authorityInfo = new ArrayList<>();
        if (!ObjectUtils.isEmpty(roles)) {
            //角色名称前添加前缀ROLE_,spring security中的要求
            roles.forEach(
                    role ->
                            authorityInfo.add(new SimpleGrantedAuthority("ROLE_" + role.getName())));
        }
        if (!ObjectUtils.isEmpty(permissions)) {
            //添加权限信息
            permissions.forEach(permission ->
                    authorityInfo.add(new SimpleGrantedAuthority(permission.getName())));
        }
        //缓存权限信息
        RedisUtil.add(key, authorityInfo.stream()
                .map(SimpleGrantedAuthority::getAuthority)
                //使用逗号进行分割组合成为权限字符串
                .collect(Collectors.joining(",")));
        return authorityInfo;
    }

    @Override
    public UserDetailsEntity getUserDetail(String username) {
        return (UserDetailsEntity) loadUserByUsername(username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.getUser(username);
        if (ObjectUtils.isEmpty(user)) {
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        UserDetailsEntity userDetails = new UserDetailsEntity();
        userDetails.setUser(user);
        userDetails.setAuthorities(getUserAuthorityInfo(user.getId()));
        userDetails.setUser(user);
        return userDetails;
    }

}
